SPORE.$Import('core.fx.base');
SPORE.$Import('core.element.style');

/*
---

name: Fx.CSS

description: Contains the CSS animation logic. Used by Fx.Tween, Fx.Morph, Fx.Elements.

license: MIT-style license.

requires: [Fx, Element.Style]

provides: Fx.CSS

...
*/

SPORE.register('core.fx.css',function($ns){

	Fx.CSS = new Class({
	
		Extends: Fx,
	
		//prepares the base from/to object
	
		prepare: function(element, property, values){
			values = Array.from(values);
			if (values[1] == null){
				values[1] = values[0];
				values[0] = element.getStyle(property);
			}
			var parsed = values.map(this.parse);
			return {from: parsed[0], to: parsed[1]};
		},
	
		//parses a value into an array
	
		parse: function(value){
			value = Function.from(value)();
			value = (typeof value == 'string') ? value.split(' ') : Array.from(value);
			return value.map(function(val){
				val = String(val);
				var found = false;
				Object.each(Fx.CSS.Parsers, function(parser, key){
					if (found) return;
					var parsed = parser.parse(val);
					if (parsed || parsed === 0) found = {value: parsed, parser: parser};
				});
				found = found || {value: val, parser: Fx.CSS.Parsers.String};
				return found;
			});
		},
	
		//computes by a from and to prepared objects, using their parsers.
	
		compute: function(from, to, delta){
			var computed = [];
			(Math.min(from.length, to.length)).times(function(i){
				computed.push({value: from[i].parser.compute(from[i].value, to[i].value, delta), parser: from[i].parser});
			});
			computed.$family = Function.from('fx:css:value');
			return computed;
		},
	
		//serves the value as settable
	
		serve: function(value, unit){
			if (typeOf(value) != 'fx:css:value') value = this.parse(value);
			var returned = [];
			value.each(function(bit){
				returned = returned.concat(bit.parser.serve(bit.value, unit));
			});
			return returned;
		},
	
		//renders the change to an element
	
		render: function(element, property, value, unit){
			element.setStyle(property, this.serve(value, unit));
		},
	
		//searches inside the page css to find the values for a selector
	
		search: function(selector){
			if (Fx.CSS.Cache[selector]) return Fx.CSS.Cache[selector];
			var to = {}, selectorTest = new RegExp('^' + selector.escapeRegExp() + '$');
			Array.each(document.styleSheets, function(sheet, j){
				var href = sheet.href;
				if (href && href.contains('://') && !href.contains(document.domain)) return;
				var rules = sheet.rules || sheet.cssRules;
				Array.each(rules, function(rule, i){
					if (!rule.style) return;
					var selectorText = (rule.selectorText) ? rule.selectorText.replace(/^\w+/, function(m){
						return m.toLowerCase();
					}) : null;
					if (!selectorText || !selectorTest.test(selectorText)) return;
					Object.each(Element.Styles, function(value, style){
						if (!rule.style[style] || Element.ShortStyles[style]) return;
						value = String(rule.style[style]);
						to[style] = ((/^rgb/).test(value)) ? value.rgbToHex() : value;
					});
				});
			});
			return Fx.CSS.Cache[selector] = to;
		}
	
	});
	
	Fx.CSS.Cache = {};
	
	Fx.CSS.Parsers = {
	
		Color: {
			parse: function(value){
				if (value.match(/^#[0-9a-f]{3,6}$/i)) return value.hexToRgb(true);
				return ((value = value.match(/(\d+),\s*(\d+),\s*(\d+)/))) ? [value[1], value[2], value[3]] : false;
			},
			compute: function(from, to, delta){
				return from.map(function(value, i){
					return Math.round(Fx.compute(from[i], to[i], delta));
				});
			},
			serve: function(value){
				return value.map(Number);
			}
		},
	
		Number: {
			parse: parseFloat,
			compute: Fx.compute,
			serve: function(value, unit){
				return (unit) ? value + unit : value;
			}
		},
	
		String: {
			parse: Function.from(false),
			compute: function(zero, one){
				return one;
			},
			serve: function(zero){
				return zero;
			}
		}
	
	};

});